package com.dd.cloud.common.utils;

import org.springframework.data.redis.core.RedisTemplate;

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 使用redis生成唯一流水号
 *
 * @author zk
 */
public class SerialNumberUtils {
    private final SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyMMdd");
    //患者病历卡自增key 长期有效key
    private final String medKey = "health_med_no";
    //药品编码自增key 长期有效key
    private final String drugsNumKey = "health_med_no";
    //订单编号,流水key 有效期一天
    private final String repeatKey = "repeat_order_num";
    //证书编号
    private final String certificateKey = "certificate_no";
    private static SerialNumberUtils instance = null;
    private static final String[] INVITE_CODE_STR_ARR = new String[]{"Z", "1", "A", "B", "C", "D", "E", "F", "G", "H", "2", "3", "4", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "5", "6", "7", "8", "9", "I", "J", "T", "U", "V", "W", "X", "Y", "0"};

    private SerialNumberUtils() {
    }

    public static SerialNumberUtils getInstance() {
        if (instance == null) {
            synchronized (SerialNumberUtils.class) {
                if (instance == null) {
                    instance = new SerialNumberUtils();
                }
            }
        }
        return instance;
    }

    /**
     * 生成唯一流水号
     *
     * @param prefix 前缀
     * @return 唯一编号
     */
    public String generateSerialNumber(RedisTemplate<String, String> redisTemplate, String prefix) {
        // 生成编码,使用redis自增
        String key = prefix + format.format(new Date());
        Long increment = increment(redisTemplate, key);
        String ins = String.valueOf(increment);
        if (ins.length() <= 6) {
            return key + String.format("%06d", increment);
        }
        //设置key的有效期
        redisTemplate.expire(key, 1, TimeUnit.DAYS);
        return key + String.format("%0" + (ins.length() + 1) + "d", increment);
    }

    /**
     * 获取当前流水序号
     *
     * @param prefix 前缀
     * @return 唯一编号
     */
    public String getSerialNumber(RedisTemplate<String, String> redisTemplate, String prefix) {
        // 生成编码,使用redis自增
        String key = prefix + format.format(new Date());
        String number = redisTemplate.opsForValue().get(key);
        return number;
    }

    /**
     * 生成唯一患者病历卡号
     *
     * @return 唯一编号
     */
    public String medSerialNumber(RedisTemplate<String, String> redisTemplate) {
        // 生成编码,使用redis自增
        Long increment = redisTemplate.opsForValue().increment(medKey, 1);
        String ins = String.valueOf(increment);
        if (ins.length() <= 6) {
            return String.format("%06d", increment);
        }
        return String.format("%0" + ins.length() + "d", increment);
    }

    /**
     * 生成药品编码自增
     *
     * @return 唯一编号
     */
    public String drugsNumSerialNumber(RedisTemplate<String, String> redisTemplate) {
        // 生成编码,使用redis自增
        Long increment = redisTemplate.opsForValue().increment(drugsNumKey, 1);
        String ins = String.valueOf(increment);
        if (ins.length() <= 6) {
            return String.format("%06d", increment);
        }
        return String.format("%0" + ins.length() + "d", increment);
    }

    /**
     * 生成药品编码自增10位
     *
     * @return 唯一编号
     */
    public String drugsNumTenSerialNumber(RedisTemplate<String, String> redisTemplate) {
        // 生成编码,使用redis自增
        Long increment = redisTemplate.opsForValue().increment(drugsNumKey, 1);
        String ins = String.valueOf(increment);
        if (ins.length() <= 10) {
            return String.format("%010d", increment);
        }
        return String.format("%0" + ins.length() + "d", increment);
    }

    /**
     * 生成累加的重复订单编号尾号
     *
     * @return 唯一编号
     */
    public String repeatNumber(RedisTemplate<String, String> redisTemplate) {
        // 生成编码,使用redis自增
        Long increment = increment(redisTemplate, repeatKey);
        String ins = String.valueOf(increment);
        if (ins.length() > 6) {
            redisTemplate.opsForValue().set(repeatKey, String.valueOf(1));
            return String.valueOf(1);
        }
        return String.valueOf(increment);
    }

    /**
     * 生成证书编号
     *
     * @return 唯一编号
     */
    public String certificateNo(RedisTemplate<String, String> redisTemplate) {
        // 生成编码,使用redis自增
        String date = LocalDateTime.now().format(formatter);
        Long increment = redisTemplate.opsForValue().increment(date + certificateKey, 1);
        String str = date + increment;
        if (str.length() > 10) {
            return forCertificateNo(redisTemplate, LocalDateTime.now());
        }
        return date + String.format("%04d", increment);
    }

    public String forCertificateNo(RedisTemplate<String, String> redisTemplate, LocalDateTime now) {
        String date = now.plusDays(1).format(formatter);
        Long increment = redisTemplate.opsForValue().increment(date + certificateKey, 1);
        String str = date + increment;
        if (str.length() > 10) {
            return forCertificateNo(redisTemplate, now);
        }
        return date + String.format("%04d", increment);
    }

    /**
     * 序号加一
     */
    public Long increment(RedisTemplate<String, String> redisTemplate, String key) {
        //String key = prefix + format.format(new Date());
        Long increment = redisTemplate.opsForValue().increment(key, 1);
        redisTemplate.expire(key, 1, TimeUnit.DAYS);
        return increment;

    }

    /**
     * 序号减一
     */
    public Long decrement(RedisTemplate<String, String> redisTemplate, String key) {
        //String key = prefix + format.format(new Date());
        Long increment = redisTemplate.opsForValue().increment(key, -1);
        redisTemplate.expire(key, 1, TimeUnit.DAYS);
        return increment;
    }

    /**
     * 生成36位字符随机数
     * 36的len次方 唯一字符
     * 如len=4 36*36*36*36=1,679,616
     *
     * @param id
     * @param len
     * @return
     */
    public static String generatorInviteCode(Integer id, Integer len) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < len; i++) {
            int num = i;
            int product = 1;
            while (num > 0) {
                product *= 36;
                num--;
            }
            int index;
            if (product == 1) {
                index = id % 36;
            } else {
                index = (id / product) % 36;
            }
            sb.append(INVITE_CODE_STR_ARR[index]);
        }
        return sb.toString();
    }
}
